﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using HotShot.Entities.Messages;
using HotShot.Service.Entities;

namespace HotShot.Service.BusinessLogic
{
    public class MessageCorrelationLogic
    {
        public static MessageCorrelationLogic Current = new MessageCorrelationLogic();

        public Dictionary<string, CorrelationDescriptor> _correlatedMessages = new Dictionary<string, CorrelationDescriptor>();

        public void SetupCorrelation(string correlationId, List<ProcessorNodeDescriptor> submittedNodes)
        {
            _correlatedMessages[correlationId] = new CorrelationDescriptor(correlationId, submittedNodes);

        }

        public CorrelationDescriptor AddCorrelatedMessage(string correlationId, string nodeId, BaseMessage message)
        {
            if (!_correlatedMessages.ContainsKey(correlationId))
            {
                return null;
            }
            _correlatedMessages[correlationId].Responses[nodeId] = message;
            _correlatedMessages[correlationId].MessageType = message.MessageType;

            return _correlatedMessages[correlationId];
        }

        public void FinalizeCorrelation(string correlationId)
        {
            _correlatedMessages[correlationId].IsProcessed = true;
            _correlatedMessages.Remove(correlationId);
        }

        public List<CorrelationDescriptor> CollectAvailableResults(string messageType, int threshold)
        {
            return _correlatedMessages.Where(i => 
                i.Value.MessageType == messageType &&
                i.Value.ResponseReady(threshold) && 
                !i.Value.IsProcessed).Select(i => i.Value).ToList();
        }
    }

    public class CorrelationDescriptor
    {
        public static int CorrelationTimeOutMs = 1000;

        static CorrelationDescriptor() {
            int i;
            if (Int32.TryParse(ConfigurationManager.AppSettings["MessageCorrelationLogic.CorrelationTimeOutMs"], out i))
            {
                CorrelationTimeOutMs = i;
            }
        }

        public readonly Dictionary<string, BaseMessage> Responses = new Dictionary<string, BaseMessage>();
        public readonly DateTime TimeOut;
        public volatile bool IsProcessed;
        public readonly string CorrelationId;
        public string MessageType;

        public int ResponseCount { get { return Responses.Count(i => i.Value != null); } }

        public int ResponseTotal { get { return Responses.Count; } }

        public bool TimedOut { get { return TimeOut <= DateTime.Now; } }

        public CorrelationDescriptor(string correlationId, List<ProcessorNodeDescriptor> submittedNodes)
        {
            CorrelationId = correlationId;
            submittedNodes.ForEach(i => Responses.Add(i.NodeId, null));
            TimeOut = DateTime.Now.AddMilliseconds(CorrelationTimeOutMs);
        }

        internal bool ResponseReady(int threshold)
        {
            return ResponseCount == ResponseTotal || TimedOut && ResponseCount >= threshold;
        }

    }
}
